﻿
/***************************************************************************

Copyright (c) BizRulX. All rights reserved.

***************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.XPath;
using System.Collections;

namespace RuleXSoft.FrameworkLib
{
    public class ValidationEngine<T>
    {

        public static bool IsRuleMatched { get; set; }

        public static T Run<T>(string ruleFileName, T obj) where T : new()
        {
            T type = new T();
            IsRuleMatched = false;
            type = ValidateRule<T>(GetXMLNodes(ruleFileName), obj);

            return type;

        }

        static XPathNodeIterator GetXMLNodes(string fileName)
        {
            XPathNodeIterator _xmlNodes;

            XPathDocument document = new XPathDocument(fileName);
            XPathNavigator navigator = document.CreateNavigator();
            XPathNodeIterator nodes = navigator.Select("Types/RuleSetClass/RuleSet/Rule");

            _xmlNodes = nodes;

            return _xmlNodes;
        }

        private static string EvaluateExpression(string strExpr)
        {
            try
            {
                RPNParser parser = new RPNParser();

                ArrayList arrExpr = parser.GetPostFixNotation(strExpr,
                                    Type.GetType("System.Int64"), false);
                string szResult = parser.Convert2String(arrExpr);

                return parser.EvaluateRPN(arrExpr, Type.GetType("System.Int64"), null).ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        

        static T ValidateRule<T>(XPathNodeIterator nodes, T paramObj) where T : new()
        {

            try
            {
                Dictionary<string, string> _ListObj = new Dictionary<string, string>();
                Dictionary<string, string> _ListXml = new Dictionary<string, string>();

                Dictionary<string, string> _outputObj = new Dictionary<string, string>();

                string strKey = string.Empty;
                string strField = string.Empty;
                string strOperator = string.Empty;
                string strDataType = string.Empty;
                string strIsRequired = string.Empty;
                string strOutput = string.Empty;
                string strDerived = string.Empty;

                Int32 totalRecCount = 0;

                T _objXML = new T();

                Type _oType = paramObj.GetType();
                PropertyInfo[] propInfos = _oType.GetProperties();

                PropertyInfo pinfo = _oType.GetProperty("IsRequired");

                foreach (PropertyInfo propInfo in propInfos)
                {
                    //Console.WriteLine(propInfo.GetValue(paramObj, null));
                    _ListObj.Add(propInfo.Name, Convert.ToString(propInfo.GetValue(paramObj, null)));

                }

                //foreach (KeyValuePair<string, string> val in _ListObj)
                //{
                //    Console.WriteLine("Key: {0} Value: {1}", val.Key, val.Value.ToString());
                //}


                IDictionary<string, string> listKeyVal = new Dictionary<string, string>();

                while (nodes.MoveNext())
                {
                    //Console.WriteLine("Testing..." + nodes.Current.Name);
                    if (nodes.Current.MoveToFirstChild())
                    {
                        do
                        {
                            strKey = nodes.Current.GetAttribute("key", string.Empty);
                            strField = nodes.Current.GetAttribute("value", string.Empty);
                            strOperator = nodes.Current.GetAttribute("operator", string.Empty);
                            strDataType = nodes.Current.GetAttribute("datatype", string.Empty);
                            strOutput = nodes.Current.GetAttribute("direction", string.Empty);
                            strDerived = nodes.Current.GetAttribute("derived", string.Empty);
                            //Console.WriteLine("Node Name: {0} Value: {1} Data Type: {2} operator: {3} direction: {4}",
                            //    strKey,
                            //    strField,
                            //    strDataType,
                            //    strOperator,
                            //    strOutput);

                            _ListXml.Add(strKey, strField);

                            //Start -- Object to xml rule matching section

                            Type _oXMLType = _objXML.GetType();
                            PropertyInfo[] xmlPropInfos = _oXMLType.GetProperties();

                            foreach (PropertyInfo propInfo in xmlPropInfos)
                            {
                                if (Convert.ToString(propInfo.Name).Equals(Convert.ToString(strKey)))
                                {
                                    if (Convert.ToString(strDataType).ToUpper().Equals("STRING"))
                                    {
                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {
                                            

                                            if (strField.ToString().Equals(string.Empty))
                                            {
                                                propInfo.SetValue(_objXML, null, null);
                                            }
                                            else
                                            {
                                                propInfo.SetValue(_objXML, strField.ToString(), null);
                                            }
                                            //propInfo.SetValue(_objXML, strField.ToString(), null);
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            propInfo.SetValue(_objXML, strField.ToString(), null);

                                            //if the value is derived one
                                            if (Convert.ToBoolean(strDerived))
                                            {

                                                StringBuilder strbFieldVal = new StringBuilder();
                                                strbFieldVal.Append(strField);
                                                foreach (KeyValuePair<string, string> item in listKeyVal)
                                                {
                                                    strbFieldVal.Replace("#" + item.Key + "#", item.Value);
                                                }
                                                _outputObj.Add(propInfo.Name, Convert.ToString(EvaluateExpression(strbFieldVal.ToString())) + "|" + strDataType);
                                            }
                                            else
                                            {
                                                _outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            }

                                            //_outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                    }
                                    if (Convert.ToString(strDataType).ToUpper().Equals("BOOLEAN"))
                                    {
                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {
                                            if (!string.IsNullOrEmpty(strField))
                                            {
                                                propInfo.SetValue(_objXML, Convert.ToBoolean(strField), null); 
                                            }
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            if (!string.IsNullOrEmpty(strField))
                                            {
                                                propInfo.SetValue(_objXML, Convert.ToBoolean(strField), null);
                                                _outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            }

                                            //_outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                    }
                                    if (Convert.ToString(strDataType).ToUpper().Equals("DOUBLE"))
                                    {
                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToDouble(strField), null);
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToDouble(strField), null);

                                            //if the value is derived one
                                            if (Convert.ToBoolean(strDerived))
                                            {

                                                StringBuilder strbFieldVal = new StringBuilder();
                                                strbFieldVal.Append(strField);
                                                foreach (KeyValuePair<string, string> item in listKeyVal)
                                                {
                                                    strbFieldVal.Replace("#" + item.Key + "#", item.Value);
                                                }
                                                _outputObj.Add(propInfo.Name, strbFieldVal.ToString() + "|" + strDataType);
                                            }
                                            else
                                            {
                                                _outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            }

                                            //_outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());


                                        }
                                    }
                                    if (Convert.ToString(strDataType).ToUpper().Equals("INT"))
                                    {
                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToInt32(strField), null);
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToInt32(strField), null);

                                            //if the value is derived one
                                            if (Convert.ToBoolean(strDerived))
                                            {

                                                StringBuilder strbFieldVal = new StringBuilder();
                                                strbFieldVal.Append(strField);
                                                foreach (KeyValuePair<string, string> item in listKeyVal)
                                                {
                                                    strbFieldVal.Replace("#" + item.Key + "#", item.Value);
                                                }
                                                _outputObj.Add(propInfo.Name, strbFieldVal.ToString() + "|" + strDataType);
                                            }
                                            else
                                            {
                                                _outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            }

                                            //_outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                    }
                                    if (Convert.ToString(strDataType).ToUpper().Equals("DATETIME"))
                                    {
                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToDateTime(strField), null);
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToDateTime(strField), null);

                                            _outputObj.Add(propInfo.Name, strField.ToString() + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                    }
                                    if (Convert.ToString(strDataType).ToUpper().Equals("LIST<OBJECT>"))
                                    {
                                        List<object> listObj = new List<object>();
                                        listObj.Add(strField);

                                        if (Convert.ToString(strOutput).ToUpper().Equals("INPUT"))
                                        {

                                            propInfo.SetValue(_objXML, listObj, null);
                                            //Console.WriteLine("INPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                        else if (Convert.ToString(strOutput).ToUpper().Equals("OUTPUT"))
                                        {
                                            propInfo.SetValue(_objXML, Convert.ToString(strField), null);

                                            _outputObj.Add(propInfo.Name, listObj + "|" + strDataType);
                                            //Console.WriteLine("OUTPUT objXML Field Name: {0} Value set: {1}", propInfo.Name, strField.ToString());
                                        }
                                    }
                                }
                            }
                            //End -- Object to xml rule matching section

                            listKeyVal.Add(new KeyValuePair<string, string>(strKey, strField));

                        } while (nodes.Current.MoveToNext());

                        #region If the provided rule object and passed object matches then set the output values for the passed object
                        ValidationAttributes<T> _sourceObj = new ValidationAttributes<T>((T)paramObj);

                        if (Convert.ToBoolean(((T)paramObj).Equals(_objXML)))
                        {
                            //Console.WriteLine("Matched...");
                            if (Convert.ToString(strDataType).ToUpper().Equals("BOOLEAN")
                                || Convert.ToString(strDataType).ToUpper().Equals("STRING")
                                || Convert.ToString(strDataType).ToUpper().Equals("DOUBLE")
                                || Convert.ToString(strDataType).ToUpper().Equals("INT")
                                || Convert.ToString(strDataType).ToUpper().Equals("DATETIME")
                                || Convert.ToString(strDataType).ToUpper().Equals("LIST<OBJECT>"))
                            {
                                foreach (KeyValuePair<string, string> fieldName in _outputObj)
                                {
                                    IList<string> kayValList = fieldName.Value.ToString().Split('|');
                                    foreach (PropertyInfo originalPropInfo in propInfos)
                                    {
                                        if (Convert.ToString(originalPropInfo.Name).Equals(Convert.ToString(fieldName.Key)))
                                        {
                                            if (Convert.ToString(kayValList[1]).ToUpper().Equals("BOOLEAN"))
                                            {
                                                originalPropInfo.SetValue(paramObj, Convert.ToBoolean(kayValList[0].ToString()), null);
                                            }
                                            else if (Convert.ToString(kayValList[1]).ToUpper().Equals("STRING"))
                                            {
                                                originalPropInfo.SetValue(paramObj, Convert.ToString(kayValList[0].ToString()), null);

                                            }
                                            else if (Convert.ToString(kayValList[1]).ToUpper().Equals("DOUBLE"))
                                            {
                                                originalPropInfo.SetValue(paramObj, Convert.ToDouble(kayValList[0].ToString()), null);
                                            }
                                            else if (Convert.ToString(kayValList[1]).ToUpper().Equals("DATETIME"))
                                            {
                                                originalPropInfo.SetValue(paramObj, Convert.ToDateTime(kayValList[0].ToString()), null);
                                            }
                                            else if (Convert.ToString(kayValList[1]).ToUpper().Equals("INT"))
                                            {
                                                originalPropInfo.SetValue(paramObj, Convert.ToInt32(kayValList[0].ToString()), null);
                                            }
                                            else if (Convert.ToString(kayValList[1]).ToUpper().Equals("LIST<OBJECT>"))
                                            {
                                                originalPropInfo.SetValue(paramObj, (object)kayValList[0].ToString(), null);
                                            }
                                        }

                                    }
                                }
                            }
                            //else if (Convert.ToString(strDataType).Equals("string"))
                            //{
                            //    foreach (KeyValuePair<string, string> fieldName in _outputObj)
                            //    {
                            //        foreach (PropertyInfo originalPropInfo in propInfos)
                            //        {
                            //            if (Convert.ToString(originalPropInfo.Name).Equals(Convert.ToString(fieldName.Key)))
                            //            {
                            //                originalPropInfo.SetValue(paramObj, Convert.ToString(fieldName.Value), null);
                            //            }

                            //        }
                            //    }
                            //}

                            IsRuleMatched = true; // Rule Matched.
                            break;
                        }
                        else
                        {
                            //Console.WriteLine("Not Matched...");
                            _outputObj.Clear();
                        }
                        #endregion

                        _ListXml.Clear();
                        listKeyVal.Clear();
                    }

                    totalRecCount++;

                }
                //Console.WriteLine("Total records count is {0}", totalRecCount);
                return (T)paramObj;
            }
            catch (Exception ex)
            {

                throw ex;
            }


        }




    }
}
